package net.pulseproject.router.modules.misc;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import net.pulseproject.commons.midi.Receivable;
import net.pulseproject.commons.midi.Transmitable;
import net.pulseproject.commons.midi.entity.MidiSocket;
import net.pulseproject.router.modules.MidiModule;
import net.pulseproject.router.modules.useable.MidiReceiverModule;
import net.pulseproject.router.modules.useable.MidiTransmitterModule;

public class ModuleFilter { // TODO actually not a filter, as not needed to extend abstract Filter class

	// TODO implement ModuleFilterTest
	
	/**
	 * Quasi filter endpoint-modules of routings.
	 */
	public final Collection<MidiReceiverModule> filterEndModules(final Collection<MidiTransmitterModule> transmitters) {
		final Collection<MidiReceiverModule> receiverModules = new HashSet<MidiReceiverModule>();
		
		for (MidiTransmitterModule module : transmitters) {
			final Collection<Receivable> outputs = module.getOutgoingModules();
			receiverModules.addAll(this.filterMidiReceiverModulesRecursive(outputs));
		}
		
		return Collections.unmodifiableCollection(receiverModules);
	}
	
	private Collection<MidiReceiverModule> filterMidiReceiverModulesRecursive(final Collection<Receivable> outputs) {
		final Collection<MidiReceiverModule> receiverModules = new HashSet<MidiReceiverModule>();
		
		for (Receivable receivable : outputs) {
			
			if(receivable instanceof MidiReceiverModule) {
				// add proper endpoint receiver module
				final MidiReceiverModule midiReceiver = (MidiReceiverModule) receivable;
				receiverModules.add(midiReceiver);
				
			} else if(receivable instanceof Transmitable) {
				// not an endpoint, continue recursion
				final Transmitable transmitter = (Transmitable) receivable;
				final Collection<Receivable> subOutputs = transmitter.getOutgoingModules();
				receiverModules.addAll(this.filterMidiReceiverModulesRecursive(subOutputs));
				
			} else {
				throw new RuntimeException("Routing contained something (typed Receivable) which was not instance of " +
					"MidiReceiverModule or Transmitable but rather [" + receivable.getClass().getName() + "]! " +
					"Instance to String: " + receivable);
			}
		}
		
		return Collections.unmodifiableCollection(receiverModules);
	}
	

	public final <T extends MidiModule> Map<String, Collection<T>> modulesByPort(final Collection<T> midiModules) {
		final Map<String, Collection<T>> groupedModules = new HashMap<String, Collection<T>>();
		
		for (final T currentModule : midiModules) {
			final MidiSocket socket = currentModule.getSocket();
			final String port = socket.getPort();
			
			if(groupedModules.containsKey(port) == false) {
				groupedModules.put(port, new HashSet<T>());
			}
			
			final Collection<T> yetStoredModules = groupedModules.get(port);
			yetStoredModules.add(currentModule);
		}
		return Collections.unmodifiableMap(groupedModules);
	}
}
